/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */

package Draw;

import AdvanceMath.Vector2;
import AdvanceMath.R2.Rect;
import java.awt.*;
import java.awt.geom.AffineTransform;
import java.awt.image.BufferedImage;
import java.awt.image.ImageObserver;
import java.awt.image.ImageProducer;
import java.io.File;
import java.io.Serializable;
import javax.imageio.ImageIO;
import javax.swing.ImageIcon;
import javax.swing.JPanel;


/*Copyright 2011 Kyle Dieter Sweeney
 * This file is part of the Sweeney Game Development Environment.

    Sweeney Game Development Environment is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.

    Sweeney Game Development Environment is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with Sweeney Game Development Environment.  If not, see <http://www.gnu.org/licenses/>.
 */
/**
 *
 * @author Kyle Maximilian Dieter Sweeney
 */

/**
 * A Class which holds a sprite, and can draw it.
 */
public class Image2D extends Image implements Serializable{
    protected BufferedImage bf;
    protected BufferedImage tinted;
    protected AffineTransform rotation;
    protected AffineTransform move;
    protected AffineTransform transform;
    protected Vector2 position;
    protected int depth;
    protected float scalex=1f;
    protected float scaley=1f;
    protected float theta=0f;
    protected String s="";
    protected Vector2 rotationAnchor;
    protected Rectangle drawnArea;
    protected Color tint;
    protected int thisID;
    protected static int IDcount=0;
    
    /**
     * Takes a string of the location in the project the images resides, and image name.
     *
     * Example: the Image called ball in folder Sprites: "Sprites/ball.png"
     * @param s -the string of the image name and location.
     */
    public Image2D(String s){
        try{
            bf=ImageIO.read(new File(s));
            position= new Vector2();
            drawnArea=new Rect(0,0,bf.getWidth(),bf.getHeight());
            tinted=bf;
        }catch(Exception e){
            e.printStackTrace();
        }
        this.s=s;
        depth=0;
        thisID=IDcount++;
    }
    /**
     * a default constructor, using the super default constructor
     */
    public Image2D(){
        depth=0;
        thisID=IDcount++;
    }
    /**
     * Sets the depth of the item
     * @param x -the depth
     */
    public void setDepth(int x){
        depth=x;
    }
    /**
     * returns the depth of the item.
     * @return -the depth
     */
    public int getDepth(){
        return depth;
    }
    /**
     * sets the position of the image
     * @param pos -the position in game world
     */
    public void setVector2(Vector2 pos){
        position=pos;
    }
    /**
     * sets the position using an X and Y
     * @param x -X coordinate
     * @param y -Y coordinate
     */
    public void setVector2(double x, double y){
        position=new Vector2(x,y);
    }
    /**
     * returns the Vector2 position
     * @return -the position
     */
    public Vector2 getPosition(){
        return position;
    }
 
    /**
     * Returns the Height of the original image
     * @return the Height of the original image
     */
    public int getHeight(){
        return bf.getHeight();
    }
    /**
     * Returns the Width of the original image
     * @return the width of the original image
     */
    public int getWidth(){
        return bf.getWidth();
    }
    /**
     * Obtains the scaled height of the image
     * @return the scaled height
     */
    public float getScaledHeight(){
        return getHeight()*scaley;
    }
    /**
     * Obtains the scaled width of the image
     * @return the scaled width
     */
    public float getScaledWidth(){
        return getWidth()*scalex;
    }
    /**
     * Sets the rotation of the sprite in radians
     * @param theta rotation in radians
     */
    public void setRotation(float theta){
        this.theta=(float)(theta%(2*Math.PI));
    }
    /**
     * Sets the Width scaling factor for the image
     * @param scalex the amount to scale width wise
     */
    public void setScaleX(float scalex){
        if(scalex<0f){
            this.scalex=0;
        }else{
            this.scalex=scalex;
        }
    }
    /**
     * Sets the Height scaling factor for the image
     * @param scaley the amount to scale height wise
     */
    public void setScaleY(float scaley){
        if(scaley<0f){
            this.scaley=0;
        }else{
            this.scaley=scaley;
        }
    }
    /**
     * Sets the area of the image to be drawn. Image space starts with the upper-left corner of the image being 0,0
     * @param r the area to be drawn
     */
    public void setDrawnArea(Rectangle r){
        drawnArea=r;
    }
    /**
     * Returns an ImageIcon of the original image
     * @return a new ImageIcon
     */
    public ImageIcon getIcon(){
        return new ImageIcon(bf);
    }
    
    /**
     * Returns the Rect which bounds this image.
     * @return Rect surrounding this image
     */
    public Rect getRectangle(){
        return new Rect((int)(position.getX()-(this.getScaledWidth()/2)), 
                (int)(position.getY()-(this.getScaledHeight()/2)), (int)(this.getScaledWidth()), (int)(this.getScaledHeight()),theta);
    }
    
    /**
     * While could be used independently, it is suggested that only the ImageCollection use this method.
     * @param g -the graphics used by the JFrame/JPanel
     * @param c -the JPanel this will be drawn in
     */
    public void Draw(Graphics2D g, JPanel c){
        Vector2 s=new Vector2((int)position.getX()-(this.getScaledWidth()/2), (int)position.getY()-(this.getScaledHeight()/2));
        move=new AffineTransform();
        rotation= new AffineTransform();
        transform = new AffineTransform();
        
        transform.setToScale(scalex, scaley);
        rotation.setToRotation(theta, this.getWidth()/2, this.getHeight()/2);
        move.setToTranslation((int)s.getX(), (int)s.getY());
        
        move.concatenate(transform);
        move.concatenate(rotation);
        
        g.drawImage(tinted.getSubimage(drawnArea.x, drawnArea.y, drawnArea.width, drawnArea.height), move, c); 
    }
    
    public void Draw(Graphics2D g, Frame c){
        Vector2 s=new Vector2((int)position.getX()-(this.getScaledWidth()/2), (int)position.getY()-(this.getScaledHeight()/2));
        move=new AffineTransform();
        rotation= new AffineTransform();
        transform = new AffineTransform();
        
        transform.setToScale(scalex, scaley);
        rotation.setToRotation(theta, this.getWidth()/2, this.getHeight()/2);
        move.setToTranslation((int)s.getX(), (int)s.getY());
        
        move.concatenate(transform);
        move.concatenate(rotation);
        
        g.drawImage(tinted.getSubimage(drawnArea.x, drawnArea.y, drawnArea.width, drawnArea.height), move, c); 
    }
    
    private Color tint(Color start, Color target, double percent){
        return new Color((int)(start.getRed() + percent * (target.getRed() - start.getRed())),
                         (int)(start.getGreen() + percent * (target.getGreen() - start.getGreen())),
                         (int)(start.getBlue() + percent * (target.getBlue() - start.getBlue())));
    }
    
    /**
     * Tints the image by 25% to the color passed in
     * @param target the color the image will be tinted
     */
    public void tint(Color target){
        if(target ==null){
            tinted=bf;
            return;
        }
        if (tint == null || !(tint.getRGB() == target.getRGB())) {
            try {
                tinted=ImageIO.read(new File(s));
            } catch (Exception ex) {
                ex.printStackTrace();
            }
            tint = new Color(target.getRGB());
            for (int x = 0; x < bf.getWidth(); x++) {
                for (int y = 0; y < bf.getHeight(); y++) {
                    tinted.setRGB(x, y, this.tint(new Color(bf.getRGB(x, y)), target, .250).getRGB());
                }
            }
        } else {
            return;
        }
    }
    /**
     * Tints the image by the percentage passed in to the color passed in. 1=100%
     * @param target color to tint to
     * @param percent percentage of tinting
     */
    public void tint(Color target, float percent){
        if(target ==null){
            tinted=bf;
            return;
        }
        if (tint == null || !(tint.getRGB() == target.getRGB())) {
            try {
                tinted=ImageIO.read(new File(s));
            } catch (Exception ex) {
                ex.printStackTrace();
            }
            tint = new Color(target.getRGB());
            for (int x = 0; x < bf.getWidth(); x++) {
                for (int y = 0; y < bf.getHeight(); y++) {
                    tinted.setRGB(x, y, this.tint(new Color(bf.getRGB(x, y)), target, percent).getRGB());
                }
            }
        } else {
            return;
        }
    }
    
    @Override
    public String toString(){
        return ""+s;
    }

    @Override
    public int getWidth(ImageObserver observer) {
        return bf.getWidth(observer);
    }

    @Override
    public int getHeight(ImageObserver observer) {
        return bf.getHeight(observer);
    }

    @Override
    public ImageProducer getSource() {
        return bf.getSource();
    }

    @Override
    public Graphics getGraphics() {
        return this.getGraphics();
    }

    @Override
    public Object getProperty(String name, ImageObserver observer) {
        return this.getProperty(name, observer);
    }
    @Override
    public boolean equals(Object o){
        if(o instanceof Image2D){
            return thisID==((Image2D)o).thisID;
        }else{
            return false;
        }
    }

    @Override
    public int hashCode() {
        int hash = 7;
        hash = 23 * hash + (this.position != null ? this.position.hashCode() : 0);
        hash = 23 * hash + this.depth;
        hash = 23 * hash + Float.floatToIntBits(this.scalex);
        hash = 23 * hash + Float.floatToIntBits(this.scaley);
        hash = 23 * hash + Float.floatToIntBits(this.theta);
        hash = 23 * hash + (this.s != null ? this.s.hashCode() : 0);
        hash = 23 * hash + (this.tint != null ? this.tint.hashCode() : 0);
        hash = 23 * hash + this.thisID;
        return hash;
    }
}
